home *** CD-ROM | disk | FTP | other *** search
/ Enter 2006 September / Enter 09 2006.iso / Internet / SpamExperts Home 1.1 / SpamExperts Home.exe / lib / spamexperts.modules / dns / tokenizer.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2006-07-14  |  11.5 KB  |  430 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.4)
  3.  
  4. '''Tokenize DNS master file format'''
  5. import cStringIO
  6. import sys
  7. import dns.exception as dns
  8. import dns.name as dns
  9. _DELIMITERS = {
  10.     ' ': True,
  11.     '\t': True,
  12.     '\n': True,
  13.     ';': True,
  14.     '(': True,
  15.     ')': True,
  16.     '"': True }
  17. _QUOTING_DELIMITERS = {
  18.     '"': True }
  19. EOF = 0
  20. EOL = 1
  21. WHITESPACE = 2
  22. IDENTIFIER = 3
  23. QUOTED_STRING = 4
  24. COMMENT = 5
  25. DELIMITER = 6
  26.  
  27. class UngetBufferFull(dns.exception.DNSException):
  28.     '''Raised when an attempt is made to unget a token when the unget
  29.     buffer is full.'''
  30.     pass
  31.  
  32.  
  33. class Tokenizer(object):
  34.     """A DNS master file format tokenizer.
  35.  
  36.     A token is a (type, value) tuple, where I{type} is an int, and
  37.     I{value} is a string.  The valid types are EOF, EOL, WHITESPACE,
  38.     IDENTIFIER, QUOTED_STRING, COMMENT, and DELIMITER.
  39.     
  40.     @ivar file: The file to tokenize
  41.     @type file: file
  42.     @ivar ungotten_char: The most recently ungotten character, or None.
  43.     @type ungotten_char: string
  44.     @ivar ungotten_token: The most recently ungotten token, or None.
  45.     @type ungotten_token: (int, string) token tuple
  46.     @ivar multiline: The current multiline level.  This value is increased
  47.     by one every time a '(' delimiter is read, and decreased by one every time
  48.     a ')' delimiter is read.
  49.     @type multiline: int
  50.     @ivar quoting: This variable is true if the tokenizer is currently
  51.     reading a quoted string.
  52.     @type quoting: bool
  53.     @ivar eof: This variable is true if the tokenizer has encountered EOF.
  54.     @type eof: bool
  55.     @ivar delimiters: The current delimiter dictionary.
  56.     @type delimiters: dict
  57.     @ivar line_number: The current line number
  58.     @type line_number: int
  59.     @ivar filename: A filename that will be returned by the L{where} method.
  60.     @type filename: string
  61.     """
  62.     
  63.     def __init__(self, f = sys.stdin, filename = None):
  64.         '''Initialize a tokenizer instance.
  65.  
  66.         @param f: The file to tokenize.  The default is sys.stdin.
  67.         This parameter may also be a string, in which case the tokenizer
  68.         will take its input from the contents of the string.
  69.         @type f: file or string
  70.         @param filename: the name of the filename that the L{where} method
  71.         will return.
  72.         @type filename: string
  73.         '''
  74.         if isinstance(f, str):
  75.             f = cStringIO.StringIO(f)
  76.             if filename is None:
  77.                 filename = '<string>'
  78.             
  79.         elif filename is None:
  80.             if f is sys.stdin:
  81.                 filename = '<stdin>'
  82.             else:
  83.                 filename = '<file>'
  84.         
  85.         self.file = f
  86.         self.ungotten_char = None
  87.         self.ungotten_token = None
  88.         self.multiline = 0
  89.         self.quoting = False
  90.         self.eof = False
  91.         self.delimiters = _DELIMITERS
  92.         self.line_number = 1
  93.         self.filename = filename
  94.  
  95.     
  96.     def _get_char(self):
  97.         '''Read a character from input.
  98.         @rtype: string
  99.         '''
  100.         if self.ungotten_char is None:
  101.             if self.eof:
  102.                 c = ''
  103.             else:
  104.                 c = self.file.read(1)
  105.                 if c == '':
  106.                     self.eof = True
  107.                 elif c == '\n':
  108.                     self.line_number += 1
  109.                 
  110.         else:
  111.             c = self.ungotten_char
  112.             self.ungotten_char = None
  113.         return c
  114.  
  115.     
  116.     def where(self):
  117.         '''Return the current location in the input.
  118.  
  119.         @rtype: (string, int) tuple.  The first item is the filename of
  120.         the input, the second is the current line number.
  121.         '''
  122.         return (self.filename, self.line_number)
  123.  
  124.     
  125.     def _unget_char(self, c):
  126.         '''Unget a character.
  127.  
  128.         The unget buffer for characters is only one character large; it is
  129.         an error to try to unget a character when the unget buffer is not
  130.         empty.
  131.         
  132.         @param c: the character to unget
  133.         @type c: string
  134.         @raises UngetBufferFull: there is already an ungotten char
  135.         '''
  136.         if self.ungotten_char is not None:
  137.             raise UngetBufferFull
  138.         
  139.         self.ungotten_char = c
  140.  
  141.     
  142.     def skip_whitespace(self):
  143.         '''Consume input until a non-whitespace character is encountered.
  144.  
  145.         The non-whitespace character is then ungotten, and the number of
  146.         whitespace characters consumed is returned.
  147.  
  148.         If the tokenizer is in multiline mode, then newlines are whitespace.
  149.  
  150.         @rtype: int
  151.         '''
  152.         skipped = 0
  153.         while True:
  154.             c = self._get_char()
  155.             if c != ' ' and c != '\t':
  156.                 if c != '\n' or not (self.multiline):
  157.                     self._unget_char(c)
  158.                     return skipped
  159.                 
  160.             
  161.             skipped += 1
  162.  
  163.     
  164.     def get(self, want_leading = False, want_comment = False):
  165.         '''Get the next token.
  166.  
  167.         @param want_leading: If True, return a WHITESPACE token if the
  168.         first character read is whitespace.  The default is False.
  169.         @type want_leading: bool
  170.         @param want_comment: If True, return a COMMENT token if the
  171.         first token read is a comment.  The default is False.
  172.         @type want_comment: bool
  173.         @rtype: (int, string) tuple
  174.         @raises dns.exception.UnexpectedEnd: input ended prematurely
  175.         @raises dns.exception.SyntaxError: input was badly formed
  176.         '''
  177.         if self.ungotten_token is not None:
  178.             token = self.ungotten_token
  179.             self.ungotten_token = None
  180.             if token[0] == WHITESPACE:
  181.                 if want_leading:
  182.                     return token
  183.                 
  184.             elif token[0] == COMMENT:
  185.                 if want_comment:
  186.                     return token
  187.                 
  188.             else:
  189.                 return token
  190.         
  191.         skipped = self.skip_whitespace()
  192.         if want_leading and skipped > 0:
  193.             return (WHITESPACE, ' ')
  194.         
  195.         token = ''
  196.         ttype = IDENTIFIER
  197.         while True:
  198.             c = self._get_char()
  199.             if c == '' or c in self.delimiters:
  200.                 if c == '' and self.quoting:
  201.                     raise dns.exception.UnexpectedEnd
  202.                 
  203.                 if token == '' and ttype != QUOTED_STRING:
  204.                     if c == '(':
  205.                         self.multiline += 1
  206.                         self.skip_whitespace()
  207.                         continue
  208.                     elif c == ')':
  209.                         if not self.multiline > 0:
  210.                             raise dns.exception.SyntaxError
  211.                         
  212.                         self.multiline -= 1
  213.                         self.skip_whitespace()
  214.                         continue
  215.                     elif c == '"':
  216.                         if not self.quoting:
  217.                             self.quoting = True
  218.                             self.delimiters = _QUOTING_DELIMITERS
  219.                             ttype = QUOTED_STRING
  220.                             continue
  221.                         else:
  222.                             self.quoting = False
  223.                             self.delimiters = _DELIMITERS
  224.                             self.skip_whitespace()
  225.                     elif c == '\n':
  226.                         return (EOL, '\n')
  227.                     elif c == ';':
  228.                         while None:
  229.                             c = self._get_char()
  230.                             if c == '\n' or c == '':
  231.                                 break
  232.                             
  233.                             token += c
  234.                         if want_comment:
  235.                             self._unget_char(c)
  236.                             return (COMMENT, token)
  237.                         elif c == '':
  238.                             if self.multiline:
  239.                                 raise dns.exception.SyntaxError, 'unbalanced parentheses'
  240.                             
  241.                             return (EOF, '')
  242.                         elif self.multiline:
  243.                             self.skip_whitespace()
  244.                             token = ''
  245.                             continue
  246.                         else:
  247.                             return (EOL, '\n')
  248.                     else:
  249.                         token = c
  250.                         ttype = DELIMITER
  251.                 else:
  252.                     self._unget_char(c)
  253.                 break
  254.             elif self.quoting:
  255.                 if c == '\\':
  256.                     c = self._get_char()
  257.                     if c == '':
  258.                         raise dns.exception.UnexpectedEnd
  259.                     
  260.                     if c.isdigit():
  261.                         c2 = self._get_char()
  262.                         if c2 == '':
  263.                             raise dns.exception.UnexpectedEnd
  264.                         
  265.                         c3 = self._get_char()
  266.                         if c == '':
  267.                             raise dns.exception.UnexpectedEnd
  268.                         
  269.                         if not c2.isdigit() and c3.isdigit():
  270.                             raise dns.exception.SyntaxError
  271.                         
  272.                         c = chr(int(c) * 100 + int(c2) * 10 + int(c3))
  273.                     
  274.                 elif c == '\n':
  275.                     raise dns.exception.SyntaxError, 'newline in quoted string'
  276.                 
  277.             elif c == '\\':
  278.                 c = self._get_char()
  279.                 if c == '' or c not in self.delimiters:
  280.                     self._unget_char(c)
  281.                     c = '\\'
  282.                 
  283.             
  284.             token += c
  285.         if token == '' and ttype != QUOTED_STRING:
  286.             if self.multiline:
  287.                 raise dns.exception.SyntaxError, 'unbalanced parentheses'
  288.             
  289.             ttype = EOF
  290.         
  291.         return (ttype, token)
  292.  
  293.     
  294.     def unget(self, token):
  295.         '''Unget a token.
  296.  
  297.         The unget buffer for tokens is only one token large; it is
  298.         an error to try to unget a token when the unget buffer is not
  299.         empty.
  300.         
  301.         @param token: the token to unget
  302.         @type token: (int, string) token tuple
  303.         @raises UngetBufferFull: there is already an ungotten token
  304.         '''
  305.         if self.ungotten_token is not None:
  306.             raise UngetBufferFull
  307.         
  308.         self.ungotten_token = token
  309.  
  310.     
  311.     def next(self):
  312.         '''Return the next item in an iteration.
  313.         @rtype: (int, string)
  314.         '''
  315.         token = self.get()
  316.         if token[0] == EOF:
  317.             raise StopIteration
  318.         
  319.         return token
  320.  
  321.     
  322.     def __iter__(self):
  323.         return self
  324.  
  325.     
  326.     def get_int(self):
  327.         '''Read the next token and interpret it as an integer.
  328.         
  329.         @raises dns.exception.SyntaxError:
  330.         @rtype: int
  331.         '''
  332.         (ttype, value) = self.get()
  333.         if ttype != IDENTIFIER:
  334.             raise dns.exception.SyntaxError, 'expecting an identifier'
  335.         
  336.         if not value.isdigit():
  337.             raise dns.exception.SyntaxError, 'expecting an integer'
  338.         
  339.         return int(value)
  340.  
  341.     
  342.     def get_uint8(self):
  343.         '''Read the next token and interpret it as an 8-bit unsigned
  344.         integer.
  345.         
  346.         @raises dns.exception.SyntaxError:
  347.         @rtype: int
  348.         '''
  349.         value = self.get_int()
  350.         if value < 0 or value > 255:
  351.             raise dns.exception.SyntaxError, '%d is not an unsigned 8-bit integer' % value
  352.         
  353.         return value
  354.  
  355.     
  356.     def get_uint16(self):
  357.         '''Read the next token and interpret it as a 16-bit unsigned
  358.         integer.
  359.         
  360.         @raises dns.exception.SyntaxError:
  361.         @rtype: int
  362.         '''
  363.         value = self.get_int()
  364.         if value < 0 or value > 65535:
  365.             raise dns.exception.SyntaxError, '%d is not an unsigned 16-bit integer' % value
  366.         
  367.         return value
  368.  
  369.     
  370.     def get_uint32(self):
  371.         '''Read the next token and interpret it as a 32-bit unsigned
  372.         integer.
  373.                 
  374.         @raises dns.exception.SyntaxError:
  375.         @rtype: int
  376.         '''
  377.         (ttype, value) = self.get()
  378.         if ttype != IDENTIFIER:
  379.             raise dns.exception.SyntaxError, 'expecting an identifier'
  380.         
  381.         if not value.isdigit():
  382.             raise dns.exception.SyntaxError, 'expecting an integer'
  383.         
  384.         value = long(value)
  385.         if value < 0 or value > 0x100000000L:
  386.             raise dns.exception.SyntaxError, '%d is not an unsigned 32-bit integer' % value
  387.         
  388.         return value
  389.  
  390.     
  391.     def get_string(self, origin = None):
  392.         '''Read the next token and interpret it as a string.
  393.                 
  394.         @raises dns.exception.SyntaxError:
  395.         @rtype: string
  396.         '''
  397.         (ttype, t) = self.get()
  398.         if ttype != IDENTIFIER and ttype != QUOTED_STRING:
  399.             raise dns.exception.SyntaxError, 'expecting a string'
  400.         
  401.         return t
  402.  
  403.     
  404.     def get_name(self, origin = None):
  405.         '''Read the next token and interpret it as a DNS name.
  406.                 
  407.         @raises dns.exception.SyntaxError:
  408.         @rtype: dns.name.Name object'''
  409.         (ttype, t) = self.get()
  410.         if ttype != IDENTIFIER:
  411.             raise dns.exception.SyntaxError, 'expecting an identifier'
  412.         
  413.         return dns.name.from_text(t, origin)
  414.  
  415.     
  416.     def get_eol(self):
  417.         """Read the next token and raise an exception if it isn't EOL or
  418.         EOF.
  419.  
  420.         @raises dns.exception.SyntaxError:
  421.         @rtype: string
  422.         """
  423.         (ttype, t) = self.get()
  424.         if ttype != EOL and ttype != EOF:
  425.             raise dns.exception.SyntaxError, 'expected EOL or EOF, got %d "%s"' % (ttype, t)
  426.         
  427.         return t
  428.  
  429.  
  430.